﻿using Abp.Runtime.Security;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace AVISOutBound.PublicAPI.Web.Host
{
    public static class AuthConfigurer
    {
        public static void Configure(IServiceCollection services, IConfiguration configuration)
        {
            if (bool.Parse(configuration["Authentication:JwtBearer:IsEnabled"]))
            {
                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = "JwtBearer";
                    options.DefaultChallengeScheme = "JwtBearer";
                })
                .AddJwtBearer("JwtBearer", options =>
                {
                    options.Audience = configuration["Authentication:JwtBearer:Audience"];
                    // lulus:这里采用了临时的rsa证书
                    var filename = Path.Combine(Directory.GetCurrentDirectory(), "tempkey.rsa");
                    var keyFile = File.ReadAllText(filename);
                    var tempKey = JsonConvert.DeserializeObject<JObject>(keyFile, new JsonSerializerSettings { ContractResolver = new RsaKeyContractResolver() });
                    var securityKey = new RsaSecurityKey(JsonConvert.DeserializeObject<RSAParameters>(tempKey["Parameters"].ToString()));
                    securityKey.KeyId = tempKey["KeyId"].Value<string>();
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        // The signing key must match!
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = securityKey,
                        //new SymmetricSecurityKey(
                        //    Encoding.ASCII.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),

                        // Validate the JWT Issuer (iss) claim
                        ValidateIssuer = true,
                        ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],

                        // Validate the JWT Audience (aud) claim
                        ValidateAudience = true,
                        ValidAudience = configuration["Authentication:JwtBearer:Audience"],

                        // Validate the token expiry
                        ValidateLifetime = true,

                        // If you want to allow a certain amount of clock drift, set that here
                        ClockSkew = TimeSpan.Zero,
                    };

                    options.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = QueryStringTokenResolver,
                    };
                })
                .AddIdentityServerAuthentication(options =>
                {
                    // base-address of your identityserver
                    options.Authority = "http://localhost:5000";
                    options.RequireHttpsMetadata = false;
                    // name of the API resource
                    options.ApiName = "Avis.OutBoundAPI";
                    options.SaveToken = true;
                });
            }

            //var authenticationBuilder = services.AddAuthentication();

            //if (bool.Parse(configuration["Authentication:JwtBearer:IsEnabled"]))
            //{
            //    authenticationBuilder.AddJwtBearer(options =>
            //    {
            //        options.TokenValidationParameters = new TokenValidationParameters
            //        {
            //            // The signing key must match!
            //            ValidateIssuerSigningKey = true,
            //            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),

            //            // Validate the JWT Issuer (iss) claim
            //            ValidateIssuer = true,
            //            ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],

            //            // Validate the JWT Audience (aud) claim
            //            ValidateAudience = true,
            //            ValidAudience = configuration["Authentication:JwtBearer:Audience"],

            //            // Validate the token expiry
            //            ValidateLifetime = true,

            //            // If you want to allow a certain amount of clock drift, set that here
            //            ClockSkew = TimeSpan.Zero
            //        };

            //        options.Events = new JwtBearerEvents
            //        {
            //            //OnMessageReceived = QueryStringTokenResolver
            //        };
            //    });
            //}
        }

        /* This method is needed to authorize SignalR javascript client.
         * SignalR can not send authorization header. So, we are getting it from query string as an encrypted text. */
        private static Task QueryStringTokenResolver(MessageReceivedContext context)
        {
            if (!context.HttpContext.Request.Path.HasValue ||
                !context.HttpContext.Request.Path.Value.StartsWith("/signalr"))
            {
                //We are just looking for signalr clients
                return Task.CompletedTask;
            }

            var qsAuthToken = context.HttpContext.Request.Query["enc_auth_token"].FirstOrDefault();
            if (qsAuthToken == null)
            {
                //Cookie value does not matches to querystring value
                return Task.CompletedTask;
            }

            //Set auth token from cookie
            context.Token = SimpleStringCipher.Instance.Decrypt(qsAuthToken, AppConsts.DefaultPassPhrase);
            return Task.CompletedTask;
        }

        class RsaKeyContractResolver : DefaultContractResolver
        {
            protected virtual JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
            {
                JsonProperty property = base.CreateProperty(member, memberSerialization);
                property.Ignored = false;
                return property;
            }
        }
    }
}